package com.zjny.util;

import cn.hutool.core.collection.CollUtil;
import com.google.common.base.Joiner;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 集合工具类
 *
 * @author cuihaida
 * @since 2025/06/18
 */
public class CollUtils extends CollUtil {

    /**
     * 获取子列表 -> [start, end)左开右闭
     *
     * @param list  列表集合
     * @param start 开始索引
     * @param end   结束索引
     * @param <T>   元素的类型
     * @return 子列表
     */
    public static <T> List<T> getSubList(List<T> list, int start, int end) {
        if (start < 0 || end < 0) {
            return list;
        }
        if (CollUtil.isEmpty(list) || start >= end || start >= list.size()) {
            return list;
        }

        int curEnd = Math.min(end, list.size());
        return list.subList(start, curEnd);
    }

    /**
     * 删除列表中的空元素
     *
     * @param list 列表
     * @param <T>  列表元素类型
     * @return 删除后的列表
     */
    public static <T> List<T> getDeleteEmpeyList(List<T> list) {
        List<T> ans = new ArrayList<>();
        for (T item : list) {
            if (item == null || item.toString().trim().isEmpty()) {
                continue;
            }
            ans.add(item);
        }
        return ans;
    }

    /**
     * 随机获取列表中一个元素
     * @param list 列表
     * @return 列表中一个元素
     * @param <T> 列表元素类型
     */
    public static <T> T randomOne(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        Collections.shuffle(list);
        return list.get(0);
    }

    /**
     * 列表元素拼接
     * @param list 列表
     * @param symbol 分隔符
     * @return 列表元素拼接字符串
     * @param <T> 列表元素类型
     */
    public static <T> String joinBySymbol(List<T> list, String symbol) {
        return Joiner.on(symbol).join(list);
    }

    /**
     * 获取去重后的集合
     *
     * @param list 集合
     * @param <T>  指定这个是一个泛型方法
     * @return 去重后的集合
     */
    public static <T> List<T> getDistinctList(List<T> list) {
        return list.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 获取列表中的最大值。
     * 如果列表为空，则返回0。
     *
     * @param list 包含整数的列表，不能为null。
     * @return 列表中的最大值，如果列表为空返回0。
     */
    public static int getMaxValue(List<Integer> list) {
        // 使用流对列表中的整数求最大值，如果列表为空返回0
        return list.stream().max(Integer::compareTo).orElse(0);
    }


    /**
     * 获取给定列表中的最小值。
     * 如果列表为空，则返回0。
     *
     * @param list 包含整数的列表，不能为null。
     * @return 列表中的最小值，如果列表为空返回0。
     */
    public static int getMinValue(List<Integer> list) {
        // 使用流对列表中的整数求最小值，如果列表为空返回0
        return list.stream().min(Integer::compareTo).orElse(0);
    }

    /**
     * 计算两个列表的交集。
     *
     * @param list1 第一个列表，用于找出与其他列表共有的元素。
     * @param list2 第二个列表，用于找出与其他列表共有的元素。
     * @param <T>   列表元素的类型。
     * @return 返回一个包含两个列表共有的元素的列表。
     */
    public static <T> List<T> intersectionList(List<T> list1, List<T> list2) {
        if (list1 == null || list2 == null) {
            throw new IllegalArgumentException("list1和list2不能为null");
        }
        // 通过流对list1进行过滤，只保留list2中也存在的元素，然后收集到一个新的列表中
        return list1.stream().filter(list2::contains).collect(Collectors.toList());
    }

    /**
     * 计算两个集合的交集。
     *
     * @param set1 第一个集合，不能为null。
     * @param set2 第二个集合，不能为null。
     * @return 返回一个包含两个集合共有的元素的集合，返回集合的元素类型与输入集合相同。
     */
    public static <T> Set<T> intersectionSet(Set<T> set1, Set<T> set2) {
        if (set1 == null || set2 == null) {
            throw new IllegalArgumentException("set1和set2不能为null");
        }
        // 通过流的方式，筛选出set1中set2也包含的元素，然后集合化
        return set1.stream().filter(set2::contains).collect(Collectors.toSet());
    }

    /**
     * 计算两个Map的交集，并返回一个新的Map。
     * 该方法会返回一个包含两个输入Map共有的键值对的Map。
     * 注意：该方法不考虑键值对的顺序。
     *
     * @param map1 第一个输入的Map，不能为null。
     * @param map2 第二个输入的Map，不能为null。
     * @return 一个包含map1和map2共有的键值对的新Map。
     * @throws IllegalArgumentException 如果map1或map2为null，抛出此异常。
     */
    public static <K, V> Map<K, V> intersectionMap(Map<K, V> map1, Map<K, V> map2) {
        // 检查输入的map是否为null
        if (map1 == null || map2 == null) {
            throw new IllegalArgumentException("map1和map2不能为null");
        }

        // 获取map1和map2的交集的key集合
        Set<K> intersectionKeys = intersectionSet(map1.keySet(), map2.keySet());

        // 创建一个新的map，用于存储交集
        Map<K, V> intersectionMap = new HashMap<>();

        // 遍历交集的key集合，获取对应的value值，并添加到交集map中
        for (K key : intersectionKeys) {
            intersectionMap.put(key, map1.get(key));
        }
        return intersectionMap;
    }

    /**
     * 将两个列表合并为一个新列表，新列表中包含两个输入列表中的所有不重复元素。
     *
     * @param list1 第一个输入列表，可以为null。
     * @param list2 第二个输入列表，可以为null。
     * @return 合并后的列表，包含两个输入列表中的所有不重复元素。如果其中一个列表为null，将返回另一个列表作为结果。
     */
    public static <T> List<T> unionList(List<T> list1, List<T> list2) {
        // 如果list1为null，则直接返回list2作为结果
        if (list1 == null) {
            return list2;
        }
        // 如果list2为null，则直接返回list1作为结果
        if (list2 == null) {
            return list1;
        }
        // 使用Stream API将两个列表合并，并去除重复元素，最后收集到一个新的List中返回
        return Stream.concat(list1.stream(), list2.stream()).distinct().collect(Collectors.toList());
    }

    /**
     * 将两个Set集合合并并去除重复元素，返回一个新的Set集合。
     * 如果其中一个Set为null，将返回另一个Set作为结果。
     *
     * @param set1 第一个Set集合
     * @param set2 第二个Set集合
     * @return 合并后的Set集合，不含重复元素
     */
    public static <T> Set<T> unionSet(Set<T> set1, Set<T> set2) {
        // 如果set1为null，则直接返回set2作为结果
        if (set1 == null) {
            return set2;
        }
        // 如果set2为null，则直接返回set1作为结果
        if (set2 == null) {
            return set1;
        }
        // 使用Stream API将两个集合合并，并去除重复元素，最后收集到一个新的Set中返回
        return Stream.concat(set1.stream(), set2.stream()).collect(Collectors.toSet());
    }

    /**
     * 将两个Map合并成一个新的Map。
     * 注意：如果两个Map有相同的键，新Map将保留map1中该键对应的值。
     *
     * @param map1 第一个要合并的Map，其键值对将首先被添加到新Map中
     * @param map2 第二个要合并的Map，其键值对将被添加到map1之后
     * @return 一个包含map1和map2所有键值对的新Map
     */
    public static <K, V> Map<K, V> unionMap(Map<K, V> map1, Map<K, V> map2) {
        // 初始化新Map，容量为map1和map2大小之和，以减少扩容操作
        Map<K, V> map = new HashMap<>(map1.size() + map2.size());
        // 将map1的所有键值对添加到新Map中
        map.putAll(map1);
        // 将map2的所有键值对添加到新Map中，如果存在相同键，map1的值将被保留
        map.putAll(map2);
        return map;
    }

    /**
     * 从第一个列表中减去第二个列表中的元素，返回一个新的列表。
     * 注意：此方法不改变原始列表的内容。
     *
     * @param list1 第一个列表，将从中减去第二个列表的元素。
     * @param list2 第二个列表，其元素将从第一个列表中移除。
     * @return 一个新的列表，包含在list1中存在但不在list2中的元素。
     * @throws IllegalArgumentException 如果list1或list2为null，抛出此异常。
     */
    public static <T> List<T> subtractList(List<T> list1, List<T> list2) {
        // 检查输入的list是否为null
        if (list1 == null || list2 == null) {
            throw new IllegalArgumentException("list1和list2不能为null");
        }
        // 使用Stream API对list1进行过滤，只保留list2中不存在的元素，然后收集到一个新的列表中
        return list1.stream().filter(element -> !list2.contains(element)).collect(Collectors.toList());
    }

    /**
     * 从一个集合中减去另一个集合的元素，返回一个新的集合。
     *
     * @param set1 第一个集合，作为减法的被减数。
     * @param set2 第二个集合，作为减法的减数。它的元素将从set1中移除。
     * @return 返回一个新的集合，包含在set1中但不在set2中的元素。
     */
    public static <T> Set<T> subtractSet(Set<T> set1, Set<T> set2) {
        // 使用stream对set1进行遍历，并通过filter移除set2中包含的元素
        return set1.stream().filter(element -> !set2.contains(element)).collect(Collectors.toSet());
    }

    /**
     * 从map1中减去map2，返回一个新的map，该map包含map1中不在map2中的键值对。
     *
     * @param map1 第一个map，作为减法操作的被减数。
     * @param map2 第二个map，作为减法操作的减数。
     * @return 返回一个新的map，包含map1中存在但map2中不存在的键值对。
     */
    public static <K, V> Map<K, V> subtractMap(Map<K, V> map1, Map<K, V> map2) {
        // 初始化一个新map，大小预计为map1和map2大小之和
        Map<K, V> map = new HashMap<>(map1.size() + map2.size());

        // 当map1和map2都不为空时进行处理
        if (CollUtils.isNotEmpty(map1) && CollUtils.isNotEmpty(map2)) {
            // 复制map1和map2的键集合用于后续比较
            Set<K> setKey1 = new HashSet<>(map1.keySet());
            Set<K> setKey2 = new HashSet<>(map2.keySet());

            // 从map1键的集合中移除map2中存在的键
            for (K k : setKey2) {
                setKey1.remove(k);
            }

            // 将剩余在map1中但不在map2中的键值对添加到新map中
            for (K k : setKey1) {
                map.put(k, map1.get(k));
            }
        }
        return map;
    }
}
